import queue
import threading
import time
from datetime import datetime

import serial
import serial.tools.list_ports
import ttkbootstrap as ttkb
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.util.device_data_validator import DeviceValidator
from app.view.base_view import View


@annotate(ClassInfo(name="FtHwAu", category="ft_tool"))
class FtSeriaHwAulView(View):
    """设置视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)

        # 串口相关变量
        self.ser = None
        self.is_connected = False
        self.receive_queue = queue.Queue()
        self.test_receive_queue = queue.Queue()
        self.testing_in_progress = False

        # 测试流程配置
        self.test_steps = [
            "开始写入",
            "SN读取验证",
            "重启设备",
            "测试完成"
        ]
        self.current_step = -1

        # 创建主布局
        self._create_main_layout()

        # 启动接收线程
        self.receive_thread = threading.Thread(target=self._receive_data, daemon=True)
        self.receive_thread.start()

        # 定期检查接收队列
        self._check_receive_queue()

        # 初始搜索串口
        self._search_ports()

    def _create_main_layout(self):
        """创建主主界面布局"""
        # 顶部配置区域
        config_frame = ttkb.LabelFrame(self, text="串口配置", padding=10)
        config_frame.pack(fill=X, padx=10, pady=5)

        # 串口选择
        port_frame = ttkb.Frame(config_frame)
        port_frame.pack(fill=X, pady=5)

        ttkb.Label(port_frame, text="串口:").pack(side=LEFT, padx=5)
        self.port_var = ttkb.StringVar()
        self.port_combo = ttkb.Combobox(port_frame, textvariable=self.port_var, width=15)
        self.port_combo.pack(side=LEFT, padx=5)

        ttkb.Button(port_frame, text="搜索", command=self._search_ports).pack(side=LEFT, padx=5)
        self.connect_btn = ttkb.Button(port_frame, text="打开", command=self._toggle_connection, bootstyle=SUCCESS)
        self.connect_btn.pack(side=LEFT, padx=5)

        # 波特率等参数
        params_frame = ttkb.Frame(config_frame)
        params_frame.pack(fill=X, pady=5)

        ttkb.Label(params_frame, text="波特率:").pack(side=LEFT, padx=5)
        self.baudrate_var = ttkb.StringVar(value="115200")
        baudrate_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.baudrate_var,
            values=["9600", "19200", "38400", "57600", "115200", "230400"],
            width=10
        )
        baudrate_combo.pack(side=LEFT, padx=5)

        ttkb.Label(params_frame, text="数据位:").pack(side=LEFT, padx=5)
        self.databits_var = ttkb.StringVar(value="8")
        databits_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.databits_var,
            values=["5", "6", "7", "8"],
            width=5
        )
        databits_combo.pack(side=LEFT, padx=5)

        ttkb.Label(params_frame, text="停止位:").pack(side=LEFT, padx=5)
        self.stopbits_var = ttkb.StringVar(value="1")
        stopbits_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.stopbits_var,
            values=["1", "1.5", "2"],
            width=5
        )
        stopbits_combo.pack(side=LEFT, padx=5)

        ttkb.Label(params_frame, text="校验位:").pack(side=LEFT, padx=5)
        self.parity_var = ttkb.StringVar(value="None")
        parity_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.parity_var,
            values=["None", "Odd", "Even"],
            width=7
        )
        parity_combo.pack(side=LEFT, padx=5)

        # SN输入区域
        sn_frame = ttkb.LabelFrame(self, text="SN输入", padding=10)
        sn_frame.pack(fill=X, padx=10, pady=5)

        ttkb.Label(sn_frame, text="扫描SN:").pack(side=LEFT, padx=5)
        self.sn_var = ttkb.StringVar()
        self.sn_entry = ttkb.Entry(sn_frame, textvariable=self.sn_var, width=50, font=("Arial", 12))
        self.sn_entry.pack(side=LEFT, padx=5, fill=X, expand=True)
        self.sn_entry.bind("<Return>", self._on_sn_entered)  # 回车触发

        ttkb.Button(sn_frame, text="开始测试", command=self._start_test).pack(side=LEFT, padx=5)
        ttkb.Button(sn_frame, text="清除", command=lambda: self.sn_var.set("")).pack(side=LEFT, padx=5)

        # 中间区域：时间轴和日志
        middle_frame = ttkb.Frame(self)
        middle_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)

        # 左侧：时间轴
        self.timeline_frame = ttkb.LabelFrame(middle_frame, text="测试流程", padding=10)
        self.timeline_frame.pack(side=LEFT, fill=Y, padx=(0, 5))
        self._init_timeline()

        # 右侧：日志
        log_frame = ttkb.LabelFrame(middle_frame, text="测试日志", padding=10)
        log_frame.pack(side=RIGHT, fill=BOTH, expand=True, padx=(5, 0))

        self.log_text = ttkb.Text(log_frame, wrap=WORD, state=DISABLED)
        self.log_text.pack(fill=BOTH, expand=True)

        # 配置日志颜色标签
        self.log_text.tag_config("info", foreground="white")
        self.log_text.tag_config("success", foreground="#4CAF50")
        self.log_text.tag_config("error", foreground="#ff4444")
        self.log_text.tag_config("command", foreground="#00C851")
        self.log_text.tag_config("response", foreground="#33b5e5")

        # 底部控制区域
        control_frame = ttkb.Frame(self, padding=10)
        control_frame.pack(fill=X, padx=10, pady=5)

        self.status_var = ttkb.StringVar(value="就绪")
        self.status_label = ttkb.Label(control_frame, textvariable=self.status_var, bootstyle=INFO, anchor=W)
        self.status_label.pack(side=LEFT, fill=X, expand=True)

        ttkb.Button(control_frame, text="清空日志", command=self._clear_log).pack(side=RIGHT, padx=5)
        ttkb.Button(control_frame, text="停止测试", command=self._stop_test, bootstyle=DANGER).pack(side=RIGHT, padx=5)

    def _init_timeline(self):
        """初始化时间轴"""
        # 清除现有内容
        for widget in self.timeline_frame.winfo_children():
            widget.destroy()

        # 创建时间轴节点
        for i, step in enumerate(self.test_steps):
            self._create_timeline_node(i, step, "pending")

    def _create_timeline_node(self, index, text, status):
        """创建时间轴节点"""
        # 节点容器
        node_frame = ttkb.Frame(self.timeline_frame)
        node_frame.pack(fill=X, pady=10)

        # 时间轴线和点
        line_frame = ttkb.Frame(node_frame, width=20)
        line_frame.pack(side=LEFT, fill=Y)

        # 连接线（不是第一个节点）
        if index > 0:
            line = ttkb.Frame(
                line_frame,
                height=20,
                width=2,
                bootstyle=SECONDARY
            )
            line.pack(anchor=CENTER)

        # 状态点
        if status == "pending":
            point_style = LIGHT
            point_text = "-"
        elif status == "current":
            point_style = PRIMARY
            point_text = "*"
        elif status == "success":
            point_style = SUCCESS
            point_text = "P"
        else:  # error
            point_style = DANGER
            point_text = "N"

        ttkb.Label(
            line_frame,
            text=point_text,
            bootstyle=point_style,
            font=("Arial", 12)
        ).pack(anchor=CENTER)

        # 节点文本
        ttkb.Label(
            node_frame,
            text=text,
            font=("Arial", 10)
        ).pack(side=LEFT, padx=5, anchor=CENTER)

        # 保存节点引用
        setattr(self, f"timeline_node_{index}", node_frame)

    def _update_timeline(self, step, status):
        """更新时间轴状态"""
        if 0 <= step < len(self.test_steps):
            # 先销毁旧节点
            node = getattr(self, f"timeline_node_{step}", None)
            if node:
                node.destroy()
            # 创建新节点
            self._create_timeline_node(step, self.test_steps[step], status)

    def _search_ports(self):
        """搜索可用串口"""
        self.port_combo['values'] = []
        ports = serial.tools.list_ports.comports()
        port_list = [port.device for port in ports]
        if port_list:
            self.port_combo['values'] = port_list
            self.port_combo.current(0)
            self._add_log("已搜索到可用串口", "info")

    def _toggle_connection(self):
        """切换串口连接状态"""
        if self.is_connected:
            # 关闭串口
            self._disconnect()
        else:
            # 打开串口
            self._connect()

    def _connect(self):
        """连接串口"""
        try:
            port = self.port_var.get()
            if not port:
                self._add_log("请选择串口", "error")
                return

            baudrate = int(self.baudrate_var.get())

            # 数据位转换
            databits = int(self.databits_var.get())
            if databits == 5:
                databits = serial.FIVEBITS
            elif databits == 6:
                databits = serial.SIXBITS
            elif databits == 7:
                databits = serial.SEVENBITS
            else:
                databits = serial.EIGHTBITS

            # 停止位转换
            stopbits = float(self.stopbits_var.get())
            if stopbits == 1:
                stopbits = serial.STOPBITS_ONE
            elif stopbits == 1.5:
                stopbits = serial.STOPBITS_ONE_POINT_FIVE
            else:
                stopbits = serial.STOPBITS_TWO

            # 校验位转换
            parity = self.parity_var.get().lower()
            if parity == "none":
                parity = serial.PARITY_NONE
            elif parity == "odd":
                parity = serial.PARITY_ODD
            else:  # even
                parity = serial.PARITY_EVEN

            # 打开串口
            self.ser = serial.Serial(
                port=port,
                baudrate=baudrate,
                bytesize=databits,
                stopbits=stopbits,
                parity=parity,
                timeout=1
            )

            if self.ser.is_open:
                self.is_connected = True
                self.connect_btn.config(text="关闭", bootstyle=DANGER)
                self._add_log(f"已连接到 {port}，波特率 {baudrate}", "success")
                self.status_var.set(f"已连接: {port}")
                # 禁用配置项
                self.port_combo.config(state=DISABLED)
                self.baudrate_var.set(str(baudrate))

        except Exception as e:
            self._add_log(f"连接失败: {str(e)}", "error")

    def _disconnect(self):
        """断开串口连接"""
        if self.ser and self.ser.is_open:
            self.ser.close()

        self.is_connected = False
        self.connect_btn.config(text="打开", bootstyle=SUCCESS)
        self._add_log("已断开连接", "info")
        self.status_var.set("就绪")

        # 停止测试
        if self.testing_in_progress:
            self._stop_test()

        # 启用配置项
        self.port_combo.config(state=NORMAL)

    def _on_sn_entered(self, event=None):
        """SN输入完成（回车触发）"""
        if self.is_connected and not self.testing_in_progress:
            self._start_test()

    def _start_test(self):
        """开始测试流程"""
        sn = self.sn_var.get().strip()
        if not self.is_connected:
            self._add_log("请先连接串口", "error")
            return

        res, msg = DeviceValidator.validate_mac(sn)
        if not res:
            self._add_log(f"{msg}", "error")
            self.sn_entry.focus_set()
            return
        if len(sn) != 17 and len(sn) != 12:
            self._add_log(f"SN长度错误 len:{len(sn)}", "error")
            self.sn_entry.focus_set()
            return
        if self.testing_in_progress:
            self._add_log("测试已在进行中", "info")
            return

        self._add_log(f"开始测试流程，SN: {sn}", "info")
        self.testing_in_progress = True
        self.current_step = -1
        self.test_sn = sn

        # 重置时间轴
        self._init_timeline()

        # 启动测试线程
        threading.Thread(target=self._test_flow, daemon=True).start()

    def _stop_test(self):
        """停止测试流程"""
        if self.testing_in_progress:
            self.testing_in_progress = False
            self._add_log("测试已停止", "info")
            self.status_var.set("测试已停止")
            if self.current_step >= 0:
                self._update_timeline(self.current_step, "error")

    def _test_flow(self):
        """测试流程主函数"""
        try:
            # 步骤1: SN写入
            self.current_step = 0
            self._update_timeline(self.current_step, "current")
            self.status_var.set(f"测试中: {self.test_steps[self.current_step]}")
            self._add_log(f"开始{self.test_steps[self.current_step]}", "info")

            # 发送AT命令写入SN
            cmd_sn = (self.test_sn.replace('-', '')
                      .replace(' ', '')
                      .replace(':', "")
                      .upper())
            write_command = f"AT+MAC={cmd_sn}\r\n"
            success = self._send_at_command(write_command, "+MAC", 1)

            if not success:
                raise Exception(f"{self.test_steps[self.current_step]}失败")

            self._update_timeline(self.current_step, "success")
            self._add_log(f"{self.test_steps[self.current_step]}成功", "success")

            # 步骤2: SN读取验证
            self.current_step = 1
            self._update_timeline(self.current_step, "current")
            self.status_var.set(f"测试中: {self.test_steps[self.current_step]}")
            self._add_log(f"开始{self.test_steps[self.current_step]}", "info")

            # 发送AT命令读取SN
            read_command = "AT+MAC=?\r\n"
            cmd_sn = self.test_sn.replace(' ', '').replace(':', '-').upper()
            response = self._send_at_command(read_command,
                                             f"{cmd_sn[0]}{cmd_sn[1]}-{cmd_sn[2]}{cmd_sn[3]}-{cmd_sn[4]}{cmd_sn[5]}-{cmd_sn[6]}{cmd_sn[7]}-{cmd_sn[8]}{cmd_sn[9]}-{cmd_sn[10]}{cmd_sn[11]}",
                                             1)
            if not response:
                raise Exception(f"{self.test_steps[self.current_step]}失败，读取值不匹配")

            self._update_timeline(self.current_step, "success")
            self._add_log(f"{self.test_steps[self.current_step]}成功，SN匹配", "success")

            # 重启设备
            self.current_step = 2
            bt_command = "AT+RESET\r\n"
            success = self._send_at_command(bt_command, "+RESET:OK", 1)
            if not success:
                raise Exception(f"{self.test_steps[self.current_step]}失败")
            self._update_timeline(self.current_step, "success")
            self._add_log(f"{self.test_steps[self.current_step]}成功", "success")

            # 测试完成
            self.current_step = 3
            self._update_timeline(self.current_step, "success")
            self._add_log("所有测试步骤完成", "success")
            self.status_var.set("测试完成: 成功")
            self.sn_var.set("")
            self.sn_entry.focus_set()

        except Exception as e:
            self._add_log(str(e), "error")
            self._update_timeline(self.current_step, "error")
            self.status_var.set(f"测试失败: {str(e)}")
        finally:
            self.testing_in_progress = False

    def _send_at_command(self, command, expected_response, timeout=5, return_response=False):
        """发送AT命令并等待响应"""
        if not self.is_connected or not self.ser or not self.ser.is_open:
            return False

        try:
            # 清空接收缓冲区
            self.ser.flushInput()
            self.ser.flushOutput()

            # 发送命令
            self.ser.write(command.encode('utf-8'))
            self._add_log(f"发送命令: {command.strip()}", "command")

            # 等待响应
            start_time = time.time()
            response = ""

            while time.time() - start_time < timeout and self.testing_in_progress:
                if not self.test_receive_queue.empty():
                    item_type, message = self.test_receive_queue.get()
                    if item_type == "data":
                        # ToDo log
                        response += message.strip()
                        self._add_log(
                            f"Test收到响应: {response.strip()} res:{expected_response in response} expected_response {expected_response}",
                            "response")
                        if expected_response in response:
                            if return_response:
                                return response
                            return True
                    self.test_receive_queue.task_done()
                time.sleep(0.1)

            # 超时未收到预期响应
            self._add_log(f"超时未收到预期响应，实际收到: {response.strip()}", "error")
            return False
        except Exception as e:
            self._add_log(f"发送命令失败: {str(e)}", "error")
            return False

    def _receive_data(self):
        """接收数据线程"""
        while True:
            if self.is_connected and self.ser and self.ser.is_open:
                try:
                    # 读取数据
                    if self.ser.in_waiting > 0:
                        data = self.ser.read(self.ser.in_waiting)
                        try:
                            text = data.decode('utf-8')
                            if self.testing_in_progress:
                                self.test_receive_queue.put(("data", text))
                            self.receive_queue.put(("data", text))

                        except UnicodeDecodeError:
                            self.receive_queue.put(("error", "无法解码接收到的数据"))
                except Exception as e:
                    if self.is_connected:  # 只有在连接状态下才报告错误
                        self.receive_queue.put(("error", f"接收错误: {str(e)}"))
                    time.sleep(0.1)
            else:
                time.sleep(0.1)

    def _check_receive_queue(self):
        """检查接收队列并更新UI"""
        while not self.receive_queue.empty():
            item_type, message = self.receive_queue.get()
            if item_type == "data":
                self._add_log(f"收到数据: {message.strip()}", "response")
            else:
                self._add_log(message, "error")
            self.receive_queue.task_done()

        # 定期检查
        self.after(100, self._check_receive_queue)

    def _add_log(self, message, tag="info"):
        """添加日志信息"""
        timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
        log_message = f"[{timestamp}] {message}\n"

        self.log_text.config(state=NORMAL)
        self.log_text.insert(END, log_message, tag)
        self.log_text.see(END)  # 滚动到底部
        self.log_text.config(state=DISABLED)

    def _clear_log(self):
        """清空日志"""
        self.log_text.config(state=NORMAL)
        self.log_text.delete("1.0", END)
        self.log_text.config(state=DISABLED)

    def on_close(self):
        """窗口关闭时的处理"""
        self.testing_in_progress = False
        if self.is_connected:
            self._disconnect()
        self.destroy()

    def create_widgets(self):
        pass
